From fb6c8cd466de50a7cc8b9e7642f7296a887a90a5 Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Wed, 7 Jul 2021 10:47:17 -0400 Subject: [PATCH] composetable: Handle includes This adds the mechanics for parsing include lines in Compose files. We do detect and prevent cycles. Tests included. --- gtk/gtkcomposetable.c | 90 +++++++++++++++++++++++++- testsuite/gtk/compose/cycle | 3 + testsuite/gtk/compose/include | 3 + testsuite/gtk/compose/include.expected | 4 ++ testsuite/gtk/compose/included | 1 + testsuite/gtk/compose/nofile | 3 + testsuite/gtk/composetable.c | 47 ++++++++++++++ 7 files changed, 149 insertions(+), 2 deletions(-) create mode 100644 testsuite/gtk/compose/cycle create mode 100644 testsuite/gtk/compose/include create mode 100644 testsuite/gtk/compose/include.expected create mode 100644 testsuite/gtk/compose/included create mode 100644 testsuite/gtk/compose/nofile diff --git a/gtk/gtkcomposetable.c b/gtk/gtkcomposetable.c index 0c1f2d83f1..b8561aa161 100644 --- a/gtk/gtkcomposetable.c +++ b/gtk/gtkcomposetable.c @@ -49,6 +49,7 @@ gtk_compose_data_free (GtkComposeData *compose_data) typedef struct { GList *sequences; + GList *files; const char *compose_file; } GtkComposeParser; @@ -60,6 +61,7 @@ parser_new (void) parser = g_new (GtkComposeParser, 1); parser->sequences = NULL; + parser->files = NULL; parser->compose_file = NULL; return parser; @@ -69,6 +71,7 @@ static void parser_free (GtkComposeParser *parser) { g_list_free_full (parser->sequences, (GDestroyNotify) gtk_compose_data_free); + g_list_free_full (parser->files, g_free); g_free (parser); } @@ -248,6 +251,57 @@ fail: return FALSE; } +static void parser_parse_file (GtkComposeParser *parser, + const char *path); + +static void +parser_handle_include (GtkComposeParser *parser, + const char *line) +{ + const char *p; + const char *start, *end; + char *path; + + p = line + strlen ("include "); + + while (g_ascii_isspace (*p)) + p++; + + if (*p != '"') + goto error; + + p++; + + start = p; + + while (*p && *p != '"') + p++; + + if (*p != '"') + goto error; + + end = p; + + p++; + + while (g_ascii_isspace (*p)) + p++; + + if (*p && *p != '#') + goto error; + + path = g_strndup (start, end - start); + + parser_parse_file (parser, path); + + g_free (path); + + return; + +error: + g_warning ("Could not parse include: %s", line); +} + static void parser_parse_line (GtkComposeParser *parser, const char *line) @@ -259,7 +313,10 @@ parser_parse_line (GtkComposeParser *parser, return; if (g_str_has_prefix (line, "include ")) - return; + { + parser_handle_include (parser, line); + return; + } components = g_strsplit (line, ":", 2); @@ -797,15 +854,44 @@ gtk_compose_table_new_with_list (GList *compose_list, return retval; } +static char * +canonicalize_filename (const char *path) +{ + GFile *file; + char *retval; + + file = g_file_new_for_path (path); + retval = g_file_get_path (file); + + g_object_unref (file); + + return retval; +} + static void parser_parse_file (GtkComposeParser *parser, const char *compose_file) { + char *path; + // stash the name for the table hash if (parser->compose_file == NULL) parser->compose_file = compose_file; - parser_read_file (parser, compose_file); + path = canonicalize_filename (compose_file); + + if (g_list_find_custom (parser->files, path, (GCompareFunc)strcmp)) + { + g_warning ("include cycle detected: %s", compose_file); + g_free (path); + return; + } + + parser->files = g_list_prepend (parser->files, path); + + parser_read_file (parser, path); + + parser->files = g_list_remove (parser->files, path); } static GtkComposeTable * diff --git a/testsuite/gtk/compose/cycle b/testsuite/gtk/compose/cycle new file mode 100644 index 0000000000..b65eca834f --- /dev/null +++ b/testsuite/gtk/compose/cycle @@ -0,0 +1,3 @@ +include "testsuite/gtk/compose/cycle" # create an include cycle + + : "!" diff --git a/testsuite/gtk/compose/include b/testsuite/gtk/compose/include new file mode 100644 index 0000000000..a4769355a4 --- /dev/null +++ b/testsuite/gtk/compose/include @@ -0,0 +1,3 @@ +include "testsuite/gtk/compose/included" # see if this works + + : "!" diff --git a/testsuite/gtk/compose/include.expected b/testsuite/gtk/compose/include.expected new file mode 100644 index 0000000000..10fbc4418c --- /dev/null +++ b/testsuite/gtk/compose/include.expected @@ -0,0 +1,4 @@ +# n_seqs: 2 +# max_seq_len: 4 + : "\"\\" + : "!" # U21 diff --git a/testsuite/gtk/compose/included b/testsuite/gtk/compose/included new file mode 100644 index 0000000000..0d29359189 --- /dev/null +++ b/testsuite/gtk/compose/included @@ -0,0 +1 @@ + : "\"\\" diff --git a/testsuite/gtk/compose/nofile b/testsuite/gtk/compose/nofile new file mode 100644 index 0000000000..0b676e16d6 --- /dev/null +++ b/testsuite/gtk/compose/nofile @@ -0,0 +1,3 @@ +include "testsuite/gtk/compose/nosuchfile" # this file does not exist + + : "!" diff --git a/testsuite/gtk/composetable.c b/testsuite/gtk/composetable.c index 4348dfbea0..1414a7c99f 100644 --- a/testsuite/gtk/composetable.c +++ b/testsuite/gtk/composetable.c @@ -117,6 +117,50 @@ compose_table_compare (gconstpointer data) g_free (expected); } +static void +compose_table_cycle (void) +{ + if (g_test_subprocess ()) + { + char *file; + GtkComposeTable *table; + + file = g_build_filename (g_test_get_dir (G_TEST_DIST), "compose", "cycle", NULL); + + table = gtk_compose_table_new_with_file (file); + g_assert_nonnull (table); + g_free (file); + + return; + } + + g_test_trap_subprocess (NULL, 0, 0); + g_test_trap_assert_stderr ("*include cycle detected*"); + g_test_trap_assert_failed (); +} + +static void +compose_table_nofile (void) +{ + if (g_test_subprocess ()) + { + char *file; + GtkComposeTable *table; + + file = g_build_filename (g_test_get_dir (G_TEST_DIST), "compose", "nofile", NULL); + + table = gtk_compose_table_new_with_file (file); + g_assert_nonnull (table); + g_free (file); + + return; + } + + g_test_trap_subprocess (NULL, 0, 0); + g_test_trap_assert_stderr ("*No such file or directory*"); + g_test_trap_assert_failed (); +} + /* Check matching against a small table */ static void compose_table_match (void) @@ -360,6 +404,9 @@ main (int argc, char *argv[]) g_test_add_data_func ("/compose-table/codepoint", "codepoint", compose_table_compare); g_test_add_data_func ("/compose-table/multi", "multi", compose_table_compare); g_test_add_data_func ("/compose-table/strings", "strings", compose_table_compare); + g_test_add_data_func ("/compose-table/include", "include", compose_table_compare); + g_test_add_func ("/compose-table/include-cycle", compose_table_cycle); + g_test_add_func ("/compose-table/include-nofile", compose_table_nofile); g_test_add_func ("/compose-table/match", compose_table_match); g_test_add_func ("/compose-table/match-compact", compose_table_match_compact); g_test_add_func ("/compose-table/match-algorithmic", match_algorithmic); -- 2.30.2